package cn.zyl.demo.undertow.handler;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.core.MethodParameter;
import org.springframework.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.lang.NonNullApi;
import org.springframework.lang.Nullable;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.servlet.View;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;

/**
 * JspViewMethodReturnValueHandler类说明: 把一个逻辑上的视图名称解析为一个真正的视图
 * <p><a href="https://www.iteye.com/blog/asialee-2116582">诡异的InternalResourceViewResolver</a></p>
 * <p><a href="https://www.iteye.com/blog/elim-1770554">SpringMVC视图解析器</a></p>
 *
 * @author z.y.l
 * @version v1.0
 * @date 2020-09-08
 */
public class JspViewMethodReturnValueHandler implements HandlerMethodReturnValueHandler {
    @Autowired
    private ApplicationContext context;
    private final static String JSP_ROOT_PATH = "/WEB-INF/view/";
    /**
     * 此处理程序是否支持给定的{@linkplain MethodParameter 方法返回类型}。
     *
     * @param returnType 方法返回类型检查
     * @return {@code true}，如果此处理程序支持提供的返回类型； {@code false}否则
     */
    @Override
    public boolean supportsReturnType(MethodParameter returnType) {
        System.out.println("****************** JspViewMethodReturnValueHandler");
        System.out.println(returnType.getParameterName());
        System.out.println(returnType.getParameterType());
        System.out.println(returnType.toString());
        System.out.println(JspViewMethodReturnValueHandler.class.getClassLoader().getClass().getCanonicalName());
        System.out.println(returnType.getClass().getClassLoader().getClass().getCanonicalName());
        // jsp路径 /WEB-INF/view/
        System.out.println(context.getEnvironment().getProperty("server.servlet.context-path"));
        return false;
    }
    /**
     * 通过向模型添加属性并设置视图或将{@link ModelAndViewContainer＃setRequestHandled}标志设置为{@code true}来处理给定的返回值，
     * 以指示已直接处理响应。
     *
     * @param returnValue  从处理程序方法返回的值
     * @param returnType   返回值的类型。此类型必须先前已传递给必须返回{@code true}的{@link #supportsReturnType}
     * @param container 当前请求的ModelAndViewContainer
     * @param webRequest   当前请求
     * @throws Exception 如果返回值处理导致错误
     */
    @Override
    public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
                                  ModelAndViewContainer container, NativeWebRequest webRequest) throws Exception {
        System.out.println("------handleReturnValue-----");
        System.out.println(returnValue);
        if(returnValue == null){
            //该请求是否已经被处理程序内被完全处理
            container.setRequestHandled(false);
        }else if (returnValue instanceof CharSequence) {
            container.setRequestHandled(true);
            String viewName = returnValue.toString();
            container.setViewName(viewName);
            //重定向
            if (isRedirectViewName(viewName)) {
                container.setRedirectModelScenario(true);
                return;
            }
            HttpServletResponse response = webRequest.getNativeResponse(HttpServletResponse.class);
            assert response != null;
            response.setContentType("text/html; charset=UTF-8");
            Resource resource = this.context.getResource(getJspRootPath(viewName));
        } else {
            throw new UnsupportedOperationException("Unexpected return type: " + returnType.getParameterType().getName() + " in method: " + returnType.getMethod());
        }
    }

    protected boolean isRedirectViewName(String viewName) {
        return viewName.startsWith("redirect:");
    }
    protected String getJspRootPath(String name){
        return context.getEnvironment().getProperty("server.servlet.context-path") +
                JSP_ROOT_PATH + name +context.getEnvironment().getProperty("spring.mvc.view.suffix");
    }
    private static class JspResourceView  implements View {
        private final Resource resource;
        JspResourceView(Resource resource){ this.resource = resource; }
        @Override
        public String getContentType() { return MediaType.TEXT_HTML_VALUE; }
        @Override
        public void render(Map<String, ?> model,@Nullable HttpServletRequest request, HttpServletResponse response) throws Exception {
            response.setContentType(getContentType());
            FileCopyUtils.copy(this.resource.getInputStream(), response.getOutputStream());
        }
    }
}
